package com.raven.play.stream.test.javadsl;

import cn.hutool.core.util.StrUtil;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import slick.basic.BasicBackend;
import slick.basic.BasicProfile;
import slick.basic.DatabaseConfig;
import slick.util.ClassLoaderUtil;

/**
 * 用于适配Java, 改写自 slick.basic.DatabaseConfig
 */
public class SimpleDatabaseConfig<T extends BasicProfile> implements DatabaseConfig<T> {

    private static final Logger logger = LoggerFactory.getLogger(SimpleDatabaseConfig.class);

    private String path;
    private String profileName;
    private T untypedP;

    private Config rootConfig;

    public SimpleDatabaseConfig(String path) {
        this(path, ConfigFactory.load(), ClassLoaderUtil.defaultClassLoader());
    }

    public SimpleDatabaseConfig(String path, Config config, ClassLoader classLoader) {
        this.path = path;
        String basePath = StrUtil.isEmpty(this.path) ? "" : this.path + ".";
        if (StrUtil.isEmpty(this.profileName = config.getString(basePath + "profile"))) {
            String oldName;
            if (StrUtil.isEmpty(oldName = config.getString(basePath + "driver"))) {
                if ("slick.driver.DerbyDriver$".equals(oldName)) {
                    oldName = "slick.jdbc.DerbyProfile$";
                } else if ("slick.driver.H2Driver$".equals(oldName)) {
                    oldName = "slick.jdbc.H2Profile$";
                } else if ("slick.driver.HsqldbDriver$".equals(oldName)) {
                    oldName = "slick.jdbc.HsqldbProfile$";
                } else if ("slick.driver.MySQLDriver$".equals(oldName)) {
                    oldName = "slick.jdbc.MySQLProfile$";
                } else if ("slick.driver.PostgresDriver$".equals(oldName)) {
                    oldName = "slick.jdbc.PostgresProfile$";
                } else if ("slick.driver.SQLiteDriver$".equals(oldName)) {
                    oldName = "slick.jdbc.SQLiteProfile$";
                } else if ("slick.memory.MemoryDriver$".equals(oldName)) {
                    oldName = "slick.memory.MemoryProfile$";
                }
                if (oldName != null) {
                    logger.warn("Use `{}profile` instead of `{}driver`. The latter is deprecated since Slick 3.2 and will be removed.", basePath, basePath);
                    this.profileName = oldName;
                }
            }
        }

        if (StrUtil.isEmpty(this.profileName)) {
            throw new NullPointerException();
        }

        if (this.profileName.endsWith("$")) {
            try {
                //noinspection unchecked
                this.untypedP = (T) classLoader.loadClass(profileName).getField("MODULE$").get(null);
            } catch (Exception e) {
                throw new RuntimeException("Error getting instance of profile" + profileName, e);
            }
        }

        this.rootConfig = config;
    }

    @Override
    public BasicBackend.DatabaseDef db() {
        return profile().backend().createDatabase(rootConfig, (StrUtil.isEmpty(path) ? "" : path + ".") + "db");
    }

    @Override
    public Config config() {
        return StrUtil.isEmpty(path) ? rootConfig : rootConfig.getConfig(path);
    }

    @Override
    public String profileName() {
        return profileIsObject()
                ? profileName.substring(0, profileName.length() - 1)
                : profileName;
    }

    @Override
    public String driverName() {
        return DatabaseConfig.super.driverName();
    }

    @Override
    public boolean profileIsObject() {
        return profileName.endsWith("$");
    }

    @Override
    public boolean driverIsObject() {
        return DatabaseConfig.super.driverIsObject();
    }

    @Override
    public T profile() {
        return untypedP;
    }

    @Override
    public T driver() {
        return untypedP;
    }
}
